package com.hcc.repository.extension.mapper;

import com.hcc.repository.core.conditions.ICondition;
import com.hcc.repository.core.conditions.nativesql.NativeSqlCondition;
import com.hcc.repository.core.exceptions.TooManyResultException;
import com.hcc.repository.core.metadata.TableColumnInfo;
import com.hcc.repository.core.metadata.TableInfoHelper;
import com.hcc.repository.core.page.DefaultPageParam;
import com.hcc.repository.core.page.IPage;
import com.hcc.repository.core.utils.CollUtils;
import com.hcc.repository.core.utils.ReflectUtils;
import com.hcc.repository.extension.conditions.query.FunctionHelper;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 扩展的Mapper
 *
 * @author hushengjun
 * @date 2023/8/31
 */
public interface ExtMapper<T, ID extends Serializable> extends ChainMapper<T, ID> {

    /**
     * 插入或更新，通过是否传入id判断，若存在id，则查询是否存在判断
     *
     * @param entity
     * @return
     */
    default int insertOrUpdate(T entity) {
        TableColumnInfo idColumnInfo = TableInfoHelper.getIdColumnInfo(entity.getClass());
        if (idColumnInfo == null) {
            return 0;
        }

        Object idValue = ReflectUtils.getValueByGetter(entity, idColumnInfo.getField());
        if (idValue == null) {
            return insert(entity);
        }
        // 根据id查询
        Long count = this.defaultQuery()
                .select(idColumnInfo.getColumnName())
                .eq(idColumnInfo.getColumnName(), idValue)
                .count();
        if (count == 0) {
            return insert(entity);
        }

        return updateById(entity);
    }

    /**
     * 安全的获取一条记录
     *
     * @param condition
     * @return
     */
    default T selectOneSafe(ICondition<T> condition) {
        List<T> results = selectList(condition);
        if (CollUtils.isEmpty(results)) {
            return null;
        }

        return results.get(0);
    }

    /**
     * 查询对象列表
     *
     * @param condition
     * @param mapper
     * @param <R>
     * @return
     */
    default <R> List<R> selectObjs(ICondition<T> condition, Function<Object, R> mapper) {
        List<Object> results = selectObjs(condition);
        if (CollUtils.isEmpty(results)) {
            return Collections.emptyList();
        }

        return results.stream()
                .map(mapper)
                .collect(Collectors.toList());
    }

    /**
     * 查询对象
     *
     * @param condition
     * @param mapper
     * @param <R>
     * @return
     */
    default <R> R selectObj(ICondition<T> condition, Function<Object, R> mapper) {
        List<R> results = selectObjs(condition, mapper);
        if (CollUtils.isEmpty(results)) {
            return null;
        }
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

    /**
     * 查询对象列表
     *
     * @param condition
     * @param clazz
     * @param <R>
     * @return
     */
    @SuppressWarnings("unchecked")
    default <R> List<R> selectObjs(ICondition<T> condition, Class<R> clazz) {
        List<Object> results = selectObjs(condition);
        if (CollUtils.isEmpty(results)) {
            return Collections.emptyList();
        }

        return (List<R>) results.stream()
                .map(FunctionHelper.getFunction(clazz))
                .collect(Collectors.toList());
    }

    /**
     * 查询单个对象
     *
     * @param condition
     * @param clazz
     * @param <R>
     * @return
     */
    default <R> R selectObj(ICondition<T> condition, Class<R> clazz) {
        List<R> results = selectObjs(condition, clazz);
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

    /**
     * 分页查询
     *
     * @param condition
     * @param curPage
     * @param pageSize
     * @return
     */
    default IPage<T> selectPage(ICondition<T> condition, long curPage, long pageSize) {
        return selectPage(condition, DefaultPageParam.of(curPage, pageSize));
    }

    /**
     * 滚动分页查询
     *
     * @param condition
     * @param curPage
     * @param pageSize
     * @return
     */
    default List<T> selectListScroll(ICondition<T> condition, long curPage, long pageSize) {
        return selectListScroll(condition, DefaultPageParam.of(curPage, pageSize));
    }

    /**
     * 执行sql更新语句
     *
     * @param sql
     * @param paramMap
     * @return
     */
    default int executeUpdate(String sql, Map<String, Object> paramMap) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.putParamMap(paramMap);

        return update(condition);
    }

    /**
     * 执行sql更新语句
     *
     * @param sql
     * @param args
     * @return
     */
    default int executeUpdate(String sql, Object... args) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.addArg(args);

        return update(condition);
    }

    /**
     * 执行sql查询语句
     *
     * @param sql
     * @param paramMap
     * @return
     */
    default List<T> executeQuery(String sql, Map<String, Object> paramMap) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.putParamMap(paramMap);

        return selectList(condition);
    }

    /**
     * 执行sql查询语句
     *
     * @param sql
     * @param args
     * @return
     */
    default List<T> executeQuery(String sql, Object... args) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.addArg(args);

        return selectList(condition);
    }

    /**
     * sql单个查询
     *
     * @param sql
     * @param paramMap
     * @return
     */
    default T executeQuerySingle(String sql, Map<String, Object> paramMap) {
        List<T> results = executeQuery(sql, paramMap);
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

    /**
     * sql单个查询
     *
     * @param sql
     * @param args
     * @return
     */
    default T executeQuerySingle(String sql, Object... args) {
        List<T> results = executeQuery(sql, args);
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

    /**
     * 查询Map结果
     *
     * @param sql
     * @param paramMap
     * @return
     */
    default List<Map<String, Object>> executeQueryMaps(String sql, Map<String, Object> paramMap) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.putParamMap(paramMap);

        return selectMaps(condition);
    }

    /**
     * 查询Map结果
     *
     * @param sql
     * @param args
     * @return
     */
    default List<Map<String, Object>> executeQueryMaps(String sql, Object... args) {
        NativeSqlCondition<T> condition = new NativeSqlCondition<>();
        condition.sql(sql);
        condition.addArg(args);

        return selectMaps(condition);
    }

    /**
     * 查询单个Map结果
     *
     * @param sql
     * @param paramMap
     * @return
     */
    default Map<String, Object> executeQueryMap(String sql, Map<String, Object> paramMap) {
        List<Map<String, Object>> results = executeQueryMaps(sql, paramMap);
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

    /**
     * 查询单个Map结果
     *
     * @param sql
     * @param args
     * @return
     */
    default Map<String, Object> executeQueryMap(String sql, Object... args) {
        List<Map<String, Object>> results = executeQueryMaps(sql, args);
        if (results.size() > 1) {
            throw new TooManyResultException(1, results.size());
        }

        return results.stream()
                .findFirst()
                .orElse(null);
    }

}
